package znet

import (
	"fmt"
	"net"
	"zinx/utils"
	ziface "zinx/zInterface"
)

//iServer接口的实现，定义一个Server的服务器模块
type Server struct{
	//服务器的名字
	Name string
	//服务器的IP版本
	IPVersion string
	//服务器监听的IP
	IP string
	//服务器监听的端口号
	Port int
	//当前server的消息管理模块，用来绑定msgID和对应的处理业务API关系
	MsgHandler ziface.IMsgHandle
	//当前server的消息管理器
	ConnManager ziface.IConnManager

	onConnStart func(ziface.IConnection)
	onConnStop func(ziface.IConnection)
}

//初始化Server模块的方法
func NewServer(name string)ziface.IServer{
	s := &Server{
		Name: utils.GlobalObject.Name,
		IPVersion: "tcp4", //默认的IP版本
		IP: utils.GlobalObject.Host, //默认的IP地址
		Port: utils.GlobalObject.TcpPort, //默认的端口号
		MsgHandler: NewMsgHandle(), 
		ConnManager:NewConnManager(),
	}

	//生成后，将他赋值给全局变量使用
	utils.GlobalObject.TcpServer = s

	return s
}

//启动服务器
//调用net包创建并持续监听一个tcp连接
func (s *Server)Start(){
	// 先记录一下日志
	fmt.Printf("[Zinx] Server name:%s,listen at IP:%s, Port:%d is starting\n",
		utils.GlobalObject.Name,utils.GlobalObject.Host,utils.GlobalObject.TcpPort)
	fmt.Printf("[Zinx] Version:%s,MaxConn:%d,MaxPackageSize:%d\n",
		utils.GlobalObject.Version,utils.GlobalObject.MaxConn,utils.GlobalObject.MaxPackageSize)

	//0.开启消息队列及worker工作池--这个只能开启一次
	s.MsgHandler.StartWorkerPool()

	//1.获取一个tcp的addr
	addr,err := net.ResolveTCPAddr(s.IPVersion,fmt.Sprintf("%s:%d",s.IP,s.Port))
	if err != nil{
		fmt.Printf("ResolveTCPAdd error:%v\n",err)
		return
	}

	//监听服务器的地址
	TCPListener, err := net.ListenTCP(s.IPVersion,addr)
	if err != nil{
		fmt.Printf("listen %s error:%v\n",s.IPVersion,err)
		return
	}

	//走到这里，说明监听本地端口已经成功，记录一下日志
	fmt.Printf("start zinx server success. %s success listening...\n",s.Name)

	var cID uint32 = 0

	//阻塞的等待客户端连接，处理客户端连接业务(读写)
	// 我们要持续的监听，所以用for循环
	for {
		//AcceptTCP会阻塞在这边，等待客户端连接的到来
		// 如果连接成功，则会返回一个新的连接conn，我们要另起一个协程去处理他
		conn,err := TCPListener.AcceptTCP()
		if err != nil{
			//连接失败，记录一下日志，继续循环监听等待其他的连接请求
			fmt.Printf("AcceptTCP error:%v\n",err)
			continue
		}
		//在创建新连接之前，先看下当前的连接数是否已经达到服务器允许的最大值
		if s.ConnManager.Len() >= utils.GlobalObject.MaxConn{
			//TODO 做一些事情，比如告知客户端连接错误等
			fmt.Printf("=====> Too many connections MaxConn = %d\n",utils.GlobalObject.MaxConn)

			//把刚刚创建好的连接关闭掉
			conn.Close()
			continue
		}
		//将处理新连接的方法和新连接conn进行绑定，得到我们的连接模块
		c := NewConnection(s,conn,cID,s.MsgHandler)
		cID++

		// 另起一个协程(这个协程是针对这个连接的)，让我们的连接模块启动起来
		//这里为什么要另起一个协程？
		//答案是为了不影响AcceptTCP函数的阻塞，我们的主协程应专注于等待监听客户端的连接请求
		go c.Start()
	}
}

//关闭服务器
func (s *Server)Stop(){
	// TODO将服务器的资源，状态或者一些已经开辟的连接信息进行回收
	fmt.Printf("[STOP] server name = %s\n",s.Name)
	//释放各种连接
	s.ConnManager.Clear()
}

//运行服务器
func (s *Server)Serve(){
	go func() {
		//另起一个协程启动server的服务功能
		s.Start()
	}()
	
	//TODO，我们可以做点启动服务器之外的其他的事情，比如注册等

	//用select让主线程保持阻塞状态
	select{}
}

//路由功能：给当前的服务注册一个路由方法，供客户端的连接处理使用
func (s *Server)AddRouter(msgID uint32,router ziface.IRouter){
	s.MsgHandler.AddRouter(msgID,router)
	fmt.Println("add handle success")
}

//获得ConnManager
func (s *Server)GetConnManager()ziface.IConnManager{
	return s.ConnManager
}

//注册OnConnStart 钩子函数的方法
func (s *Server)SetOnConnStart(fn func(ziface.IConnection)){
	s.onConnStart = fn
}
//注册OnConnStop 钩子函数的方法
func (s *Server)SetOnConnStop(fn func(ziface.IConnection)){
	s.onConnStop = fn
}
//调用OnConnStart 钩子函数的方法
func (s *Server)CallOnConnStart(conn ziface.IConnection){
	s.onConnStart(conn)
}
//调用OnConnStop 钩子函数的方法
func (s *Server)CallOnConnStop(conn ziface.IConnection){
	s.onConnStop(conn)
}